package com.ra.util;


import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.ServletActionContext;
import org.springframework.stereotype.Service;

import com.espirit.eap.sa.LoginUser;
import com.espirit.eap.sa.LoginUserService;
import com.espirit.eap.sa.UserLocation;
import com.googlecode.cswish.annotation.Rest;
import com.googlecode.cswish.model.PageInfo;
import com.googlecode.cswish.oosearch.QLInfo;
import com.googlecode.cswish.struts.spring.GenericService;
import com.googlecode.cswish.util.WebsiteUtil;
import com.opensymphony.xwork2.ActionContext;
import com.ra.landlord.Zone;
import com.ra.util.baidu.BaiduAddress;
import com.ra.util.baidu.BaiduApiUtils;
import com.ra.util.baidu.BaiduPlaceDetail;
import com.ra.util.baidu.IP138Util;

/**
 * 地址服务
 * 
 * @author colin
 * @date 2014年11月28日
 *
 */
@Service
public class AddressService {
	
	private static final Log logger = LogFactory.getLog(AddressService.class);
	
	@Resource
	private GenericService genericService;
	
	@Resource
	private AreaService areaService;
	

	@Resource
	private LoginUserService loginUserService;
	
	@Resource
	private AddressCodeService addressCodeService;
	
	
	//@Resource
	//private LandlordUserService landlordUserService;
	
	//地址 关键字
	private static String[] ADDRESS_KEY = new String[]{"区","镇","乡","庄","村","路","街","道","园","市场","广场","城","苑"};
	
	/**
	 * 优先以全路径去查zone,如果没有匹配的，则去掉name，用省市区编码查zone，
	 * 以后会优化查最优zone的算法
	 * @param model
	 * @return
	 */
	@Rest(type="search")
	public Zone getZone(Address model) {
		Zone zone = null;
		//去除地址全路径的省市区
		if (model.getProvince() != null && model.getCity() != null && model.getDistrict() != null) {
			String areaCode = AreaParser.CHINA_ID + "_" + model.getProvince() + "_"
					+ model.getCity() + "_" + model.getDistrict();
			AddressCode addressCode = areaService.getAddressCode(areaCode);
			if (model.getName() != null) {
				if(addressCode != null && StringUtils.isNotBlank(addressCode.getFullName())) {
					String name = model.getName().replace(addressCode.getFullName(), "");
					model.setName(name);
				}
			}
		}
		
		List<Address> lstAddress= genericService.searchAll(model).getData();
		
		if (lstAddress == null || lstAddress.size() == 0) {
			//清空name条件
			Address addressCondition = new Address();
			addressCondition.setProvince(model.getProvince());
			addressCondition.setCity(model.getCity());
			addressCondition.setDistrict(model.getDistrict());
			
			//按区域编码查询
			lstAddress= genericService.searchAll(addressCondition).getData();
		}
		
		if (lstAddress != null && lstAddress.size() > 0) {
			Address address = getOptimalAddress(lstAddress,model);
			
			zone = getZoneOfAddress(address);
			
			if(zone == null){
				if(model.getZone1() == null){
					zone = new Zone();
					String addressFirstName = getFirstAddress(address);
					zone.setName(addressFirstName);
					address.setZone1(zone);
				}
				address = genericService.merge(address);
				zone = address.getZone1();
			}
		}
		return zone;
    }
	
	/**
	 * 返回一个address最优的zone
	 * @param address
	 * @return
	 */
	private Zone getZoneOfAddress(Address address){
		Zone zone = null;
		if (address.getZone1() != null) {
			zone = address.getZone1();
		}else if(address.getZone2() != null){
			zone = address.getZone2();
		}else if(address.getZone3() != null){
			zone = address.getZone3();
		}
		return zone;
	}
	
	/**
	 * 获取最优的地址
	 * @param source
	 * @param condition
	 * @return
	 */
	private Address getOptimalAddress(List<Address> source, Address condition){
		Address address = source.get(0);
		String addressFirstName = getFirstAddress(condition);
		if (StringUtils.isBlank(addressFirstName)) {
			addressFirstName = "";
		}
		for (Address sourceAddress : source) {
			
			Zone zone = getZoneOfAddress(address);
			if(zone != null){
				address = sourceAddress;
			}
			
			if(sourceAddress.getName().startsWith(addressFirstName)){
				address = sourceAddress;
				break;
			}
		}
		
		//暂时给第一个address，以后可在这里优化匹配最优的地址
		//address = source.get(0);
		return address;
	}
	
	/**
	 * 获取address_name的第一个地名
	 * @param address
	 * @return
	 */
	public String getFirstAddress(Address address){
		String addressName = address.getName();
		if (StringUtils.isNotBlank(addressName)) {
			for (int i = 0; i < ADDRESS_KEY.length; i++) {
				if(addressName.indexOf(ADDRESS_KEY[i]) > -1){
					addressName = addressName.split(ADDRESS_KEY[i])[0];
					break;
				}
			}
		}
		return addressName;
	}

	/**
	 * 以IP获取当前用户的地址
	 * 
	 * @return
	 */
	@Rest(simpleServiceName = "anonymity", type = "free")
	public Address getUserAddressByIp() {
		Address address = new Address();
		String ip = getRemoteIp(ServletActionContext.getRequest());
		if (logger.isDebugEnabled()) {
			logger.debug("Get location by IP: " + ip);
		}
		BaiduAddress baiduAddress = BaiduApiUtils.ipLocation(ip);
		if (baiduAddress != null) {
			address.setFromIp(Boolean.TRUE);
			address.setLongitude(baiduAddress.getLongitude());
			address.setLatitude(baiduAddress.getLatitude());
			
			// 未能精确到地区, 尝试以给定的经纬度做精确区域
			if (StringUtils.isEmpty(baiduAddress.getDistrict())) {
				baiduAddress = BaiduApiUtils.findAddressByCoordinate(baiduAddress.getLongitude(), baiduAddress.getLatitude());
			}
		}
		return address;
	}
	
	/**
	 * 更新当前用户的经纬度并获取当前用户地址
	 * 
	 * @param longitude
	 * @param latitude
	 * @return
	 */
	@Rest(simpleServiceName = "anonymity", type = "free")
	public Address updateUserCurrentAddress(Double longitude, Double latitude) {
		LoginUser loginUser = loginUserService.getLoginUser();
		UserLocation location;
		if (loginUser != null) {
			location = loginUser.getUserLocation();
			if (location == null) {
				location = new UserLocation();
				loginUser.setUserLocation(location);
			}
		} else {
			logger.debug("update anonymity user location");
			HttpSession session = ServletActionContext.getRequest().getSession();
			location = (UserLocation)session.getAttribute(RaConstant.SESSION_USER_LOCATION);
			if (location == null) {
				location = new UserLocation();
				session.setAttribute(RaConstant.SESSION_USER_LOCATION, location);
			}
		}
		
		location.setLongitude(longitude);
		location.setLatitude(latitude);
		if (logger.isDebugEnabled()) {
			Integer userId = null;
			if (loginUser != null) {
				userId = loginUser.getId();
			}
			logger.debug("Update user location to " + location.getLatitude() + ", " + location.getLongitude() 
					+ ", current user is " + userId);
		}
		return getUserCurrentAddress(true);
	}
	
	/**
	 * 获取用户的当前地址
	 * 
	 * @param disableIpLocation 
	 * @return
	 */
	@Rest(simpleServiceName = "anonymity", type = "free")
	public Address getUserCurrentAddress(boolean disableIpLocation) {
		BaiduAddress baiduAddress = null;
		Address address = new Address();
		
		// 1. weixin的实时位置
		LoginUser loginUser = loginUserService.getLoginUser();
		UserLocation location;
		if (loginUser != null) {
			location = loginUser.getUserLocation();
		} else {
			location = (UserLocation)ServletActionContext.getRequest().getSession().getAttribute(RaConstant.SESSION_USER_LOCATION);
		}
		if (location != null) {
			address.setLongitude(location.getLongitude());
			address.setLatitude(location.getLatitude());
			baiduAddress = BaiduApiUtils.findAddressByCoordinate(location.getLongitude(), location.getLatitude());
		}
		
		// 2. ip定位
		// 注：ip的定位很不精确
		if (baiduAddress == null && !disableIpLocation) {
			String ip = getRemoteIp(ServletActionContext.getRequest());
			if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip) || ip != null && ip.startsWith("192.168.")) {
				ip = IP138Util.getExternalIp();
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Get location by IP: " + ip);
			}
			baiduAddress = BaiduApiUtils.ipLocation(ip);
			if (baiduAddress != null) {
				address.setFromIp(Boolean.TRUE);
				address.setLongitude(baiduAddress.getLongitude());
				address.setLatitude(baiduAddress.getLatitude());
				ActionContext.getContext().getValueStack().setValue("ipLocation", Boolean.TRUE);
				
				// 未能精确到地区, 尝试以给定的经纬度做精确区域
				if (StringUtils.isEmpty(baiduAddress.getDistrict())) {
					baiduAddress = BaiduApiUtils.findAddressByCoordinate(baiduAddress.getLongitude(), baiduAddress.getLatitude());
				}
			}
		}
		
		if (baiduAddress != null) {
			// 将百度地址转成内部地址编码
			String[] codes = AreaParser.findAreaCode(baiduAddress.getProvince(), baiduAddress.getCity(), baiduAddress.getDistrict());
			
			if (codes[0] != null) {
				address.setProvince(new Integer(codes[0]));
			}
			if (codes[1] != null) {
				address.setCity(new Integer(codes[1]));
			}
			if (codes[2] != null) {
				address.setDistrict(new Integer(codes[2]));
			}
			return address;
		} else {
			return null;
		}
	}
	
	private String getRemoteIp(HttpServletRequest request) {
		return WebsiteUtil.getRemoteIp(request);
	}
	
	public void fixAddressInfo() {
		String ql = "from " + Address.class.getName() + " a where a.longitude is null or a.latitude is null or a.addressCode is null order by a.province, a.city, a.district";
		List<Address> addressList = genericService.searchByQl(new QLInfo(ql, true)).getData();
		List<AddressCode> lastDistrictCodeList = null;
		Integer lastAddressCodeId = null;
		for (Address address : addressList) {
			if (address.getLatitude() == null || address.getLongitude() == null) {
				String city = AreaParser.getAreaName(AreaParser.buildAreaCode(address.getProvince(), address.getCity(), address.getDistrict()));
				BaiduAddress baiduAddress = BaiduApiUtils.findCoordinateByAddress(city, address.getName());
				address.setLatitude(baiduAddress.getLatitude());
				address.setLongitude(baiduAddress.getLongitude());
			}
			
			if (address.getAddressCode() == null) {
				// find the closest AddressCode
				AddressCode addressCode = AreaParser.getAddressCode(AreaParser.buildAreaCode(address.getProvince(), address.getCity(), address.getDistrict()));
				
				// 1. all the address in the distinct
				List<AddressCode> districtCodeList;
				if (lastAddressCodeId != null && lastAddressCodeId.equals(addressCode.getId())) {
					// use last cache
					districtCodeList = lastDistrictCodeList;
				} else {
					String addressCodeQl = "from " + AddressCode.class.getName() + " a where a.parent.id=?";
					QLInfo qlInfo = new QLInfo(addressCodeQl, false, addressCode.getId());
					districtCodeList = genericService.searchByQl(qlInfo).getData();
					
					lastAddressCodeId = addressCode.getId();
					lastDistrictCodeList = districtCodeList;
				}
				
				// 2. sort by the distance
				// calculate the distance for the new address
				for (AddressCode code : districtCodeList) {
					double distance = Geohash.getPointDistance(address.getLatitude(), address.getLongitude(),
						code.getLatitude(), code.getLongitude());
					code.setDistance(distance);
				}
				// sort it
				Collections.sort(districtCodeList, new Comparator<AddressCode>() {

					@Override
					public int compare(AddressCode c1, AddressCode c2) {
						double gap = c1.getDistance() - c2.getDistance();
						if (gap == 0) {
							return 0;
						} else {
							if (gap > 0) {
								return 1;
							} else {
								return -1;
							}
						}
					}
				});
				
				// use the closest one
				AddressCode matchedAddressCode = districtCodeList.get(0);
				address.setAddressCode(matchedAddressCode);
				genericService.merge(matchedAddressCode);
			}
		}
	}
	
	/**
	 * 通过AddressCode 补全Address
	 * 
	 * @param addressCodeId
	 * @param address
	 * @return
	 */
	public Address completeAddress(Address address){
		if (address != null && address.getAddressCode() != null && address.getAddressCode().getId() != null) {
			AddressCode addressCode = genericService.load(AddressCode.class, address.getAddressCode().getId());
			address.setProvince(addressCodeService.getCodeByLevel(addressCode, AddressCode.LEVEL_PROVINCE));
			address.setCity(addressCodeService.getCodeByLevel(addressCode, AddressCode.LEVEL_CITY));
			address.setDistrict(addressCodeService.getCodeByLevel(addressCode, AddressCode.LEVEL_DISTRICT));
		}
		return address;
	}
	
	/**
	 * 根据关键字全国检索地址
	 * 
	 * @param keyword 关键字
	 * @param region 所属城市/区域名称或代号 
	 * @param output 输出格式 
	 * @return pageInfo
	 */
	public List<Address> searchBaiduPlaceByKeyword(String keyword, String region, 
			String output, int pageNo, int pageSize) {
		PageInfo<BaiduPlaceDetail> pageInfo = BaiduApiUtils.searchPlaceByKeyword(keyword, 
				region, output, pageNo, pageSize);
		List<Address> addressList = new ArrayList<>();
		if (pageInfo != null) {
			for (BaiduPlaceDetail baiduPlaceDetail : pageInfo.getData()) {
				Address address = convertBaidu(baiduPlaceDetail);
				if (address.getCity() != null && address.getProvince() != null) {
					addressList.add(address);
				}
			}
		}
		return addressList;
	}
	
	public Address convertBaidu(BaiduPlaceDetail baiduAddress) {
		// 构建一条新的地址
		Address address = new Address();
		address.setName(baiduAddress.getAddress());
		if (address.getName() == null) {
			address.setName(baiduAddress.getName());
		}

		String[] codes = AreaParser.findAreaCode(baiduAddress.getProvince(), baiduAddress.getCity(), baiduAddress.getDistrict());
		if (codes[0] != null) {
			address.setProvince(new Integer(codes[0]));
		}
		if (codes[1] != null) {
			address.setCity(new Integer(codes[1]));
		}
		if (codes[2] != null) {
			address.setDistrict(new Integer(codes[2]));
		}
		
		
		// 更新原地址的经纬度 (补充数据)
		if (address.getLatitude() == null || address.getLongitude() == null) {
			address.setLongitude(baiduAddress.getLongitude());
			address.setLatitude(baiduAddress.getLatitude());
		}
		
		// FIXME: 
		// address.setAddressCode(addressCode);
		return address;
	}
	
	public Address convertBaidu(BaiduAddress baiduAddress) {
		// 构建一条新的地址
		Address address = new Address();
		address.setName(baiduAddress.getAddress());

		String[] codes = AreaParser.findAreaCode(baiduAddress.getProvince(), baiduAddress.getCity(), baiduAddress.getDistrict());
		if (codes[0] != null) {
			address.setProvince(new Integer(codes[0]));
		}
		if (codes[1] != null) {
			address.setCity(new Integer(codes[1]));
		}
		if (codes[2] != null) {
			address.setDistrict(new Integer(codes[2]));
		}
		
		// 更新原地址的经纬度 (补充数据)
		if (address.getLatitude() == null || address.getLongitude() == null) {
			address.setLongitude(baiduAddress.getLongitude());
			address.setLatitude(baiduAddress.getLatitude());
		}
		return address;
	}
	
	public Address convertAddressCode(AddressCode addressCode) {
		Address address = new Address();
		address.setAddressCode(addressCode);
		address.setProvince(addressCodeService.getCodeByLevel(addressCode, AddressCode.LEVEL_PROVINCE));
		address.setCity(addressCodeService.getCodeByLevel(addressCode, AddressCode.LEVEL_CITY));
		address.setDistrict(addressCodeService.getCodeByLevel(addressCode, AddressCode.LEVEL_DISTRICT));
		address.setName(addressCode.getName());
		address.setLatitude(addressCode.getLatitude());
		address.setLongitude(addressCode.getLongitude());
		return address;
	}
}